import { Component, OnInit, OnDestroy } from '@angular/core';
import * as echarts from 'echarts';

import { ChartConfigurationService } from '../../chart-configuration.service';
import { DataSourceService } from '../../data-source.service';
import { SpcChartAbnormalJudgement, pointDataCollect, List } from '../occ-rules/occrules';

@Component({
  selector: 'app-cchart',
  templateUrl: './cchart.component.html',
  styleUrls: ['./cchart.component.less']
})
export class CChartComponent implements OnInit, OnDestroy {
  rules: number = 0;
  interval: number = 0;
  chartViewPoints: number = 0;
  dataPrecision: number = 3;
  chartViewPrecision: number = 2;
  dataSourcePoints: number = 0;
  randomMin: number = 0;
  randomMax: number = 0;

  dataSourceSamplesID: number[] = [];
  dataSourceValues: number[] = [];
  dataSourceSamplesSize: number[] = [];
  samplesID: number[] = [];
  values: number[] = [];
  sigmaU: number = 0.0;
  sigmaL: number = 0.0;
  averageSampleSize: number = 0;
  averageValue: number = 0.0;
  lcl: number = 0.0;
  ucl: number = 0.0;
  numbersCount: number = 0;
  moveIndexCount: number = 0;
  timer: any;
  XData: number[] = [];
  markPointData: any = [];
  lineChart: any;
  firstTime: boolean = true;

  samplesSize: number[] = [];
  uclDataRecord: number[] = [];
  lclDataRecord: number[] = [];
  clDataRecord: number[] = [];
  thIndexPointDataCollect: any;
  defectsPointDataCollect: any;
  sampleSizePointDataCollect: any;
  clPointDataCollect: any;
  uclPointDataCollect: any;
  lclPointDataCollect: any;
  tableDataSourceCollection: any = [];
  showDataTable: boolean = false;
  xScroll: number = 100;

  minYAxis: number = 0;
  maxYAxis: number = 0;

  constructor(private chartCfgSrv: ChartConfigurationService, private dataSourceSrv: DataSourceService) {}

  ngOnInit(): void {
    this.loadChartConfig();
    this.drawChart();
    this.timer = setInterval(() => {
      this.drawChart();
    }, this.interval * 1000);
  }

  ngOnDestroy(): void {
    setTimeout(() => {
      clearInterval(this.timer);
    }, this.interval * 1000);
  }

  loadChartConfig(): void {
    this.rules = this.chartCfgSrv.getChartRules();
    this.interval = this.chartCfgSrv.getChartSampleTimeInterval();
    this.chartViewPoints = this.chartCfgSrv.getChartPoints();
    this.dataPrecision = this.chartCfgSrv.getChartDataPrecision();
    this.chartViewPrecision = this.chartCfgSrv.getChartViewPrecision();

    if (this.chartViewPoints > 15) this.xScroll = this.xScroll + Math.ceil((this.chartViewPoints - 15) / 5) * 50;

    this.dataSourcePoints = this.dataSourceSrv.getChartLength(this.chartCfgSrv.getChartType());
    this.randomMin = this.dataSourceSrv.getRandomMin();
    this.randomMax = this.dataSourceSrv.getRandomMax();

    for (var i = 0; i < this.dataSourcePoints; i++) {
      this.dataSourceSamplesID?.push(this.dataSourceSrv.getSampleID(this.chartCfgSrv.getChartType(), i));
      this.dataSourceValues?.push(this.dataSourceSrv.getSampleResultData(this.chartCfgSrv.getChartType(), i));
      this.dataSourceSamplesSize?.push(this.dataSourceSrv.getSampleSize(this.chartCfgSrv.getChartType(), i));
    }
  }

  drawChart(): void {
    this.updateChartValues();
    this.calculateYAxis();
    this.getChartAnomalyValues();
    if (this.showDataTable) {
      this.populateTable();
    }
    this.initCharts();
  }

  calculateChartBaseLine(sum: number): void {
    this.averageValue = sum / this.values?.length;
    this.sigmaU = Math.sqrt(this.averageValue);
    this.sigmaL = this.sigmaU;
    this.lcl = this.averageValue - 3 * this.sigmaU;
    this.ucl = this.averageValue + 3 * this.sigmaU;
    if (this.lcl < 0) {
      this.lcl = 0.0;
      this.sigmaL = (this.averageValue - this.lcl) / 3;
    }

    this.clDataRecord?.push(this.averageValue);
    if (this.clDataRecord?.length > this.chartViewPoints) {
      this.clDataRecord?.shift();
    }

    this.uclDataRecord?.push(this.ucl);
    if (this.uclDataRecord?.length > this.chartViewPoints) {
      this.uclDataRecord?.shift();
    }

    this.lclDataRecord?.push(this.lcl);
    if (this.lclDataRecord?.length > this.chartViewPoints) {
      this.lclDataRecord?.shift();
    }
  }

  generateRandomData(): number {
    return Math.ceil(Math.random() * (this.randomMax - this.randomMin));
  }

  updateChartValues(): void {
    var i: number = 0;
    var sum: number = 0.0;
    this.XData.length = 0;

    var randomValue: number = 0;
    if (this.values?.length < this.chartViewPoints) {
      if (this.numbersCount < this.dataSourcePoints) {
        this.values?.push(this.dataSourceValues[this.numbersCount]);
        this.samplesID?.push(this.dataSourceSamplesID[this.numbersCount]);
        this.samplesSize?.push(this.dataSourceSamplesSize[this.numbersCount]);
      } else {
        randomValue = this.generateRandomData();
        while (randomValue < 1 || randomValue > this.averageSampleSize) {
          randomValue = this.generateRandomData();
        }
        this.values?.push(randomValue);
        this.samplesSize?.push(this.averageSampleSize);
        this.samplesID?.push(this.samplesID[this.numbersCount - 1] + 1);
      }

      this.numbersCount++;
      sum = this.values?.reduce((a: number, b: number) => a + b);

      for (i = 0; i < this.chartViewPoints; i++) {
        this.XData?.push(i + 1);
      }
    } else {
      this.moveIndexCount++;
      this.values?.shift();
      this.samplesSize?.shift();
      this.samplesID?.shift();
      sum = this.values?.reduce((a: number, b: number) => a + b);
      for (i = 0; i < this.chartViewPoints - 1; i++) {
        this.XData?.push(i + 1 + this.moveIndexCount);
      }
      if (this.numbersCount < this.dataSourcePoints) {
        this.values?.push(this.dataSourceValues[this.numbersCount]);
        this.samplesID?.push(this.dataSourceSamplesID[this.numbersCount]);
        this.samplesSize?.push(this.dataSourceSamplesSize[this.numbersCount]);
        this.numbersCount++;
      } else {
        randomValue = this.generateRandomData();
        while (randomValue < 1 || randomValue > this.averageSampleSize) {
          randomValue = this.generateRandomData();
        }
        this.values?.push(randomValue);
        this.samplesSize?.push(this.averageSampleSize);
        this.samplesID?.push(this.samplesID[i - 1] + 1);
      }
      sum += this.values[i];

      this.XData?.push(i + 1 + this.moveIndexCount);
    }

    this.averageSampleSize = parseInt((this.samplesSize?.reduce((a: number, b: number) => a + b) / this.samplesSize?.length).toString());
    this.calculateChartBaseLine(sum);
  }

  getChartAnomalyValues(): void {
    this.markPointData.length = 0;
    const unormList = SpcChartAbnormalJudgement(
      this.ucl,
      this.lcl,
      this.averageValue,
      this.sigmaU,
      this.values,
      this.rules,
      this.sigmaL
    ) as any;
    for (let item of unormList.arr) {
      this.markPointData?.push({ xAxis: item.pointIndex, yAxis: item.value });
    }
  }

  calculateYAxis() {
    var minYAxis = Math.min(Math.floor(this.lcl), Math.min.apply(null, this.values));
    this.minYAxis = minYAxis < 0 ? 0 : minYAxis;
    this.maxYAxis = Math.max(Math.ceil(this.ucl), Math.max.apply(null, this.values));
  }

  initCharts(): void {
    const ec = echarts as any;
    var mainContainer: any = document.getElementById('cChart');
    var resizeMainContainer = function () {
      mainContainer.style.width = `${window.innerWidth * 0.85}px`;
    };
    resizeMainContainer();
    if (this.firstTime) {
      this.lineChart = ec.init(mainContainer);
      this.firstTime = false;
    }
    window.addEventListener('resize', () => {
      resizeMainContainer();
      this.lineChart.resize();
    });

    const lineChartOption = {
      title: {
        text: 'SPC C Chart'
      },
      tooltip: {
        trigger: 'axis'
      },
      xAxis: {
        type: 'category',
        data: this.XData,
        axisLine: {
          show: 'true',
          symbol: ['none', 'arrow'],
          lineStyle: {
            type: 'dashed'
          }
        },
        axisLabel: {
          formatter: '{value}',
          align: 'left'
        },
        axisTick: {
          alignWithLabel: true
        }
      },
      yAxis: {
        type: 'value',
        name: 'Defects',
        min: this.minYAxis,
        max: this.maxYAxis,
        splitLine: {
          show: false,
          lineStyle: {
            type: 'dashed'
          }
        },
        axisLine: {
          show: 'true',
          symbol: ['none', 'arrow'],
          lineStyle: {
            type: 'dashed'
          }
        },
        axisLabel: {
          formatter: '{value}',
          align: 'left'
        },
        nameTextStyle: {
          fontWeight: 600,
          fontSize: 18
        }
      },
      series: [
        {
          data: this.values,
          type: 'line',
          lineStyle: {
            color: '#0000FF'
          },
          markPoint: {
            symbol: 'circle',
            symbolSize: 10,
            itemStyle: {
              color: '#FF5A3B'
            },
            data: this.markPointData
          },
          markLine: {
            symbol: 'none',
            lineStyle: {
              normal: {
                type: 'solid'
              }
            },
            data: [
              {
                yAxis: this.averageValue,
                lineStyle: {
                  color: '#00FF00'
                },
                label: {
                  show: true,
                  formatter: `CL=${this.averageValue.toFixed(this.chartViewPrecision)}`
                }
              },
              {
                yAxis: this.ucl,
                lineStyle: {
                  color: '#FF0000'
                },
                label: {
                  show: true,
                  formatter: `UCL=${this.ucl.toFixed(this.chartViewPrecision)}`
                }
              },
              {
                yAxis: this.lcl,
                lineStyle: {
                  color: '#FF0000'
                },
                label: {
                  show: true,
                  formatter: `LCL=${this.lcl.toFixed(this.chartViewPrecision)}`
                }
              }
            ]
          }
        }
      ]
    };

    if (this.rules > 0) {
      var sigmaBCOverCL = {
        yAxis: this.averageValue + this.sigmaU,
        lineStyle: {
          color: '#1890ff',
          type: 'dashed'
        },
        label: {
          show: true,
          formatter: `σ=${(this.averageValue + this.sigmaU).toFixed(this.chartViewPrecision)}`
        }
      };
      lineChartOption.series[0].markLine?.data.push(sigmaBCOverCL);

      var sigmaABOverCL = {
        yAxis: this.averageValue + this.sigmaU * 2,
        lineStyle: {
          color: '#1890ff',
          type: 'dashed'
        },
        label: {
          show: true,
          formatter: `2σ=${(this.averageValue + this.sigmaU * 2).toFixed(this.chartViewPrecision)}`
        }
      };
      lineChartOption.series[0].markLine?.data.push(sigmaABOverCL);

      var sigmaBCBelowCL = {
        yAxis: this.averageValue - this.sigmaL,
        lineStyle: {
          color: '#1890ff',
          type: 'dashed'
        },
        label: {
          show: true,
          formatter: `σ=${(this.averageValue - this.sigmaL).toFixed(this.chartViewPrecision)}`
        }
      };
      lineChartOption.series[0].markLine?.data.push(sigmaBCBelowCL);

      var sigmaABBelowCL = {
        yAxis: this.averageValue - this.sigmaL * 2,
        lineStyle: {
          color: '#1890ff',
          type: 'dashed'
        },
        label: {
          show: true,
          formatter: `2σ=${(this.averageValue - this.sigmaL * 2).toFixed(this.chartViewPrecision)}`
        }
      };
      lineChartOption.series[0].markLine?.data.push(sigmaABBelowCL);
    }

    this.lineChart.setOption(lineChartOption);
  }

  viewDataTable(): void {
    this.showDataTable = !this.showDataTable;
    if (!this.showDataTable) {
      this.tableDataSourceCollection?.Clear();
      this.tableDataSourceCollection.length = 0;
    }
  }

  populateTable(): void {
    if (this.thIndexPointDataCollect == undefined) {
      this.thIndexPointDataCollect = new pointDataCollect();
      this.thIndexPointDataCollect.attribute = 'thIndex';
    } else {
      this.thIndexPointDataCollect.result.length = 0;
    }

    if (this.defectsPointDataCollect == undefined) {
      this.defectsPointDataCollect = new pointDataCollect();
      this.defectsPointDataCollect.attribute = 'Defects';
    } else {
      this.defectsPointDataCollect.result.length = 0;
    }

    if (this.sampleSizePointDataCollect == undefined) {
      this.sampleSizePointDataCollect = new pointDataCollect();
      this.sampleSizePointDataCollect.attribute = 'SamplesSize';
    } else {
      this.sampleSizePointDataCollect.result.length = 0;
    }

    if (this.clPointDataCollect == undefined) {
      this.clPointDataCollect = new pointDataCollect();
      this.clPointDataCollect.attribute = 'CL';
    } else {
      this.clPointDataCollect.result.length = 0;
    }

    if (this.uclPointDataCollect == undefined) {
      this.uclPointDataCollect = new pointDataCollect();
      this.uclPointDataCollect.attribute = 'UCL';
    } else {
      this.uclPointDataCollect.result.length = 0;
    }

    if (this.lclPointDataCollect == undefined) {
      this.lclPointDataCollect = new pointDataCollect();
      this.lclPointDataCollect.attribute = 'LCL';
    } else {
      this.lclPointDataCollect.result.length = 0;
    }

    this.samplesID?.forEach((sampleID: number) => this.thIndexPointDataCollect?.result?.push(sampleID));
    this.values?.forEach((value: number) => this.defectsPointDataCollect?.result?.push(value));
    this.samplesSize?.forEach((sampleSize: number) => this.sampleSizePointDataCollect?.result?.push(sampleSize));
    this.clDataRecord?.forEach((cl: number) => this.clPointDataCollect?.result?.push(parseFloat(cl.toFixed(this.dataPrecision))));
    this.uclDataRecord?.forEach((ucl: number) => this.uclPointDataCollect?.result?.push(parseFloat(ucl.toFixed(this.dataPrecision))));
    this.lclDataRecord?.forEach((lcl: number) => this.lclPointDataCollect?.result?.push(parseFloat(lcl.toFixed(this.dataPrecision))));

    if (this.tableDataSourceCollection?.length == 0) {
      this.tableDataSourceCollection = new List<pointDataCollect>();
    } else {
      this.tableDataSourceCollection?.Clear();
    }

    this.tableDataSourceCollection?.Add(this.thIndexPointDataCollect);
    this.tableDataSourceCollection?.Add(this.defectsPointDataCollect);
    this.tableDataSourceCollection?.Add(this.sampleSizePointDataCollect);
    this.tableDataSourceCollection?.Add(this.clPointDataCollect);
    this.tableDataSourceCollection?.Add(this.uclPointDataCollect);
    this.tableDataSourceCollection?.Add(this.lclPointDataCollect);
  }
}
